﻿namespace MapTestFramework.Common
{
    using System.IO;
    using System.Xml;
    using System.Xml.XPath;

    /// <summary>
    /// Represents a formatter that formats xml files to a convenient format for XmlUnit 
    /// </summary>
    class XmlFileFormatter
    {
        private XmlDocument Document { get; set; }
        private string DocumentPath { get; set; }

        private XmlFileFormatter(string path)
        {
            this.DocumentPath = path;
        }

        /// <summary>
        /// Formats an Xml file into a convenient format for XmlUnit including removal of the end element for empty elements.
        /// </summary>
        /// <param name="path">Path of the Xml file.</param>
        public static void Format(string path)
        {
            Format(path, true);
        }
        /// <summary>
        /// Formats an Xml file into a convenient format for XmlUnit.
        /// </summary>
        /// <param name="path">Path of the Xml file.</param>
        /// <param name="removeEndElementForEmptyElements">Boolean indicating whether to remove the end element for empty elements.</param>
        public static void Format(string path, bool removeEndElementForEmptyElements)
        {
            XmlFileFormatter formatter = new XmlFileFormatter(path);
            formatter.Format(removeEndElementForEmptyElements);
        }

        private static XmlReader CreateFormattingReader(string sourcePath)
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreWhitespace = true;
            return XmlReader.Create(sourcePath, settings);
        }
        private static XmlWriter CreateFormattingWriter(string destPath)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            return XmlWriter.Create(destPath, settings);
        }
        private static XPathNavigator GetFirstChildNavigator(XPathNavigator nav)
        {
            var child = nav.CreateNavigator();
            child.MoveToFirstChild();
            return child;
        }
        private static void SetIsEmptyToTrueForEmptyElements(XPathNavigator nav)
        {
            if (nav == null)
            {
                return;
            }

            if (string.IsNullOrEmpty(nav.InnerXml) && nav.NodeType == System.Xml.XPath.XPathNodeType.Element && nav.IsNode && !nav.IsEmptyElement)
            {
                ((XmlElement)nav.UnderlyingObject).IsEmpty = true;
            }
            if (nav.HasChildren)
            {
                SetIsEmptyToTrueForEmptyElements(GetFirstChildNavigator(nav));
            }
            if (nav.MoveToNext())
            {
                SetIsEmptyToTrueForEmptyElements(nav);
            }
        }

        private void Format(bool removeEndElementForEmptyElements)
        {
            this.ReadFileIntoDocument();
            if (removeEndElementForEmptyElements)
            {
                SetIsEmptyToTrueForEmptyElements(this.Document.CreateNavigator());
            }
            this.WriteDocument(this.DocumentPath);
            return;
        }
        private void ReadFileIntoDocument()
        {
            using (XmlReader reader = CreateFormattingReader(DocumentPath))
            {
                this.Document = new XmlDocument();
                this.Document.Load(reader);
            }
        }
        private void WriteDocument(string sourcePath)
        {
            string tempPath = Path.GetTempFileName();
            using (XmlWriter writer = CreateFormattingWriter(tempPath))
            {
                this.Document.WriteTo(writer);
                writer.Flush();
                writer.Close();
            }
            File.Copy(tempPath, sourcePath, true);
        }
    }
}
